﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.Configuration;
using System.Configuration;
using System.Configuration.Provider;
using RAD.LightOne.Data;
using System.Collections.Specialized;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Reflection;
using System.Data;
using System.IO;

namespace RAD.LightOne.Web.Security
{
	#region Script
	/* 
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Users](
	[Id] [uniqueidentifier] NOT NULL,
	[Class] [varchar](256) NOT NULL,
	[Register] [datetime] NOT NULL,
	[Username] [varchar](255) NOT NULL,
	[ApplicationName] [varchar](255) NOT NULL,
	[Email] [varchar](128) NOT NULL,
	[Comment] [varchar](255) NULL,
	[Password] [varchar](128) NOT NULL,
	[PasswordQuestion] [varchar](255) NULL,
	[PasswordAnswer] [varchar](255) NULL,
	[IsApproved] [bit] NULL,
	[LastActivityDate] [datetime] NULL,
	[LastLoginDate] [datetime] NULL,
	[LastPasswordChangedDate] [datetime] NULL,
	[CreationDate] [datetime] NULL,
	[IsOnLine] [bit] NULL,
	[IsLockedOut] [bit] NULL,
	[LastLockedOutDate] [datetime] NULL,
 CONSTRAINT [PK_Users_id] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO
	 */
	#endregion
	#region Documentation
	/// <summary>
	/// Provedor de autenticação customizado aderente ao funcionamento integrado com o LightOne.
	/// </summary>
	/// <seealso cref="http://msdn.microsoft.com/en-us/library/6tc47t75(v=vs.100).aspx"/>
	#endregion
	public sealed class LightOneMembershipProvider : MembershipProvider
	{
		#region InnerTypes

		#region Documentation
		/// <summary>
		/// Interface que define o armazenamento configurável para consultas do provider, que pode variar de banco a banco.
		/// </summary>
		#endregion
		public interface IQueryStorage
		{
			string GetAllUsers { get; }
			string FindUsersByName { get; }
			string FindUsersByEmail { get; }
		}

		#region Documentation
		/// <summary>
		/// Provedor de armazenamento de expressões para utilização interna.
		/// </summary>
		#endregion
		private static class QueryStorageProvider
		{
			private static IQueryStorage instance;
			private static object locker;

			static QueryStorageProvider()
			{
				locker = new object();
			}

			public static IQueryStorage Instance(string type)
			{
				lock(locker)
				{
					Type t= Type.GetType(type);
					if (instance == null || instance.GetType() != t)
					{
						instance = (IQueryStorage) Activator.CreateInstance(t);
					}
					return instance;
				}
			}
		}

		#region Documentation
		/// <summary>
		/// Implementação concreta do armazenamento, aderente ao Sql Server 2012.
		/// </summary>
		#endregion
		private sealed class SQL12 : IQueryStorage
		{

			public string GetAllUsers
			{
				get { return "select Id, Class from users order by username offset ({0} * {1}) rows fetch next {1} rows only"; }
			}

			public string FindUsersByName
			{
				get { return "select Id, Class from users where username like {0} order by username offset ({1} * {2}) rows fetch next {2} rows only"; }
			}

			public string FindUsersByEmail
			{
				get { return "select Id, Class from users where email like {0} order by username offset ({1} * {2}) rows fetch next {2} rows only"; }
			}

		}

		#endregion

		#region Fields

		private const string NAME = "LightOneMembershipProvider";
		private const string EVENT_LOG = "Application";
		private int newPasswordLength = 8;
		private string eventSource = NAME;
		private IQueryStorage query;

		private MachineKeySection machineKey;

		private bool pEnablePasswordReset;
		private bool pEnablePasswordRetrieval;
		private bool pRequiresQuestionAndAnswer;
		private bool pRequiresUniqueEmail;
		private int pMaxInvalidPasswordAttempts;
		private int pPasswordAttemptWindow;
		private MembershipPasswordFormat pPasswordFormat;
		private int pMinRequiredNonAlphanumericCharacters;
		private int pMinRequiredPasswordLength;
		private string pPasswordStrengthRegularExpression;
		#endregion

#if DEBUG
		private Logger logger;
#endif

		#region Constructors

		public LightOneMembershipProvider() : base()
		{
			this.query = QueryStorageProvider.Instance(typeof(SQL12).FullName);
#if DEBUG

			try
			{
				logger = new Logger(@"c:\temp\LightOneLog.txt");
			}
			catch { }
			logger.Log(typeof(LightOneMembershipProvider).FullName);
			logger.Log("public LightOneMembershipProvider");
#endif

			//this.Initialize();
		}

		#endregion

		#region Properties

		public override string Description
		{
			get
			{
				return "Provedor de dados multi-propósito";
			}
		}

		public override string ApplicationName
		{
			get;
			set;
		}

		public override bool EnablePasswordReset
		{
			get { return pEnablePasswordReset; }
		}

		public override bool EnablePasswordRetrieval
		{
			get { return pEnablePasswordRetrieval; }
		}

		public override bool RequiresQuestionAndAnswer
		{
			get { return pRequiresQuestionAndAnswer; }
		}

		public override bool RequiresUniqueEmail
		{
			get { return pRequiresUniqueEmail; }
		}

		public override int MaxInvalidPasswordAttempts
		{
			get { return pMaxInvalidPasswordAttempts; }
		}

		public override int PasswordAttemptWindow
		{
			get { return pPasswordAttemptWindow; }
		}

		public override MembershipPasswordFormat PasswordFormat
		{
			get { return pPasswordFormat; }
		}

		public override int MinRequiredNonAlphanumericCharacters
		{
			get { return pMinRequiredNonAlphanumericCharacters; }
		}

		public override int MinRequiredPasswordLength
		{
			get { return pMinRequiredPasswordLength; }
		}

		public override string PasswordStrengthRegularExpression
		{
			get { return pPasswordStrengthRegularExpression; }
		}

		public bool WriteExceptionsToEventLog
		{
			get;
			set;
		}

		public override string Name
		{
			get
			{
				return base.Name;
			}
		}
		#endregion

		#region Methods

		private string GetConfigValue(string configValue, string defaultValue)
		{
			if (String.IsNullOrWhiteSpace(configValue))
				return defaultValue;

			return configValue;
		}

		private bool isInitialized = false;
		public override void Initialize(string name, NameValueCollection config)
		{
			if (isInitialized) return;
			//this.query = QueryStorageProvider.Instance(typeof(SQL12).FullName);
#if DEBUG

			try
			{
				//logger = new Logger(@"c:\temp\LightOneLog.txt");
			}
			catch { }
			logger.Log(typeof(LightOneMembershipProvider).FullName);
			logger.Log("public LightOneMembershipProvider");
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			if (config == null) throw new ArgumentNullException("config");
			if (string.IsNullOrWhiteSpace(name))
			{
				name = eventSource;
			}
			base.Initialize(name, config);

			string typeName = GetConfigValue(config["queryStorageType"],typeof(SQL12).AssemblyQualifiedName);
			this.query = QueryStorageProvider.Instance(typeName);

			this.ApplicationName = GetConfigValue(config["applicationName"],
									  System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
			pMaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
			pPasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
			pMinRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
			pMinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
			pPasswordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
			pEnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
			pEnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
			pRequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
			pRequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
			WriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));

			string temp_format = config["passwordFormat"];
			if (temp_format == null)
			{
				temp_format = "Hashed";
			}

			switch (temp_format)
			{
				case "Hashed":
					pPasswordFormat = MembershipPasswordFormat.Hashed;
					break;
				case "Encrypted":
					pPasswordFormat = MembershipPasswordFormat.Encrypted;
					break;
				case "Clear":
					pPasswordFormat = MembershipPasswordFormat.Clear;
					break;
				case "AutoGenerate" : 
				default:
					throw new ProviderException("Password format not supported.");
			}

			// Get encryption and decryption key information from the configuration.
			Configuration cfg =
			  WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
			machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

			if (machineKey.ValidationKey.Contains("AutoGenerate"))
				if (PasswordFormat != MembershipPasswordFormat.Clear)
					throw new ProviderException("Hashed or Encrypted passwords " +
												"are not supported with auto-generated keys.");

			this.isInitialized = true;
		}

		#region Documentation
		/// <summary>
		/// Altera a senha de um usuário mediante confirmação da senha atual.
		/// </summary>
		#endregion
		public override bool ChangePassword(string username, string oldPassword, string newPassword)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			var user = MembershipUserModel.FindByUserName(username, this.ApplicationName);
			if (ValidateUser(username, oldPassword))
			{
				//user==null, usuário novo.
				ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, user == null);

				this.OnValidatingPassword(args);

				if (args.Cancel) //alteração cancelada
				{
					if (args.FailureInformation != null)
					{
						throw args.FailureInformation;
					}
					else
					{
						throw new MembershipPasswordException("Change password canceled due to new password validation failure.");
					}
				}
				else //se não cancelada
				{
					LightOneMembershipUser lUser = user;
					return lUser.ChangePassword(EncodePassword(oldPassword), EncodePassword(newPassword));
				}
			}
			else
			{
				return false;
			}
		}

		private string EncodePassword(string password)
		{
			string encodedPassword = password;

			switch (PasswordFormat)
			{
				case MembershipPasswordFormat.Clear:
					break;
				case MembershipPasswordFormat.Encrypted:
					encodedPassword =
					  Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
					break;
				case MembershipPasswordFormat.Hashed:
					HMACSHA256 hash = new HMACSHA256();
					hash.Key = HexToByte(machineKey.ValidationKey);
					encodedPassword =
					  Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
					break;
				default:
					throw new ProviderException("Unsupported password format.");
			}

			return encodedPassword;
		}

		private string UnEncodePassword(string encodedPassword)
		{
			string password = encodedPassword;

			switch (PasswordFormat)
			{
				case MembershipPasswordFormat.Clear:
					break;
				case MembershipPasswordFormat.Encrypted:
					password =
					  Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
					break;
				case MembershipPasswordFormat.Hashed:
					throw new ProviderException("Cannot unencode a hashed password.");
				default:
					throw new ProviderException("Unsupported password format.");
			}

			return password;
		}

		private byte[] HexToByte(string hexString)
		{
			byte[] returnBytes = new byte[hexString.Length / 2];
			for (int i = 0; i < returnBytes.Length; i++)
				returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
			return returnBytes;
		}


		public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			var user = MembershipUserModel.FindByUserName(username, this.ApplicationName);
			if (ValidateUser(username, password))
			{
				//user==null, usuário novo.
				ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, user == null);

					LightOneMembershipUser lUser = user;
					return lUser.ChangePasswordQuestionAndAnswer(EncodePassword(password),newPasswordQuestion,newPasswordAnswer);
			}
			else
			{
				return false;
			}
		}

		public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			try
			{
				ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);
				this.OnValidatingPassword(args);

				if (args.Cancel)
				{
					status = MembershipCreateStatus.InvalidPassword;
					return null;
				}
				if (RequiresUniqueEmail && !string.IsNullOrWhiteSpace(GetUserNameByEmail(email)))
				{
					status = MembershipCreateStatus.DuplicateEmail;
					return null;
				}
				var u = GetUser(username, false);
#if DEBUG
				logger.Log("{0}",u);
#endif
				if (u == null)
				{
					//cria novo
#if DEBUG
					logger.Log("novo");
#endif
					
					DateTime now = DateTime.Now;
					var data = new MembershipUserModel(username, email, passwordQuestion, string.Empty, isApproved, false, now, null, now, now, now, EncodePassword(passwordAnswer), EncodePassword(password), this.ApplicationName);
					data.ProviderName = base.Name;
					data.Save();
#if DEBUG
					logger.Log("salvo");
#endif
					
					status = MembershipCreateStatus.Success;
					return (LightOneMembershipUser)data;
				}
				else
				{
#if DEBUG
					logger.Log("existente");
#endif
					status = MembershipCreateStatus.DuplicateUserName;

				}
				return null;
			}
			catch (Exception except)
			{
#if DEBUG
				logger.Log(except.Message);
				logger.Log(except.StackTrace);
#endif
				this.TreatException(except, MethodBase.GetCurrentMethod().Name);
				status = MembershipCreateStatus.ProviderError;
				return null;
			}
#if DEBUG
			finally
			{
				//file.Close();
			}
#endif
		}

		public override bool DeleteUser(string username, bool deleteAllRelatedData)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			try
			{
				var user = MembershipUserModel.FindByUserName(username, this.ApplicationName);
				if (user != null)
				{
					user.Delete();
					
				}
				return true;
			}
			catch (Exception except)
			{
				TreatException(except, MethodBase.GetCurrentMethod().Name);
				return false;
			}
		}

		public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			MembershipUserCollection collection = new MembershipUserCollection();
			totalRecords = 0;
			using (DBEngine dbe = DBEngine.GetInstance())
			{
				using (IDbTransaction trx = dbe.BeginTransaction())
				{
					try
					{
						dbe.Transaction = trx;
						totalRecords = Convert.ToInt32(dbe.ExecuteScalar("select count(*) from users where email like {0}", emailToMatch));
						MembershipUserModel[] all = MembershipUserModel.FindBySql(trx, this.query.FindUsersByEmail,emailToMatch, pageIndex, pageSize);
						foreach (var a in all)
						{
							collection.Add((LightOneMembershipUser)a);
						}
					}
					catch (Exception except)
					{
						TreatException(except, MethodBase.GetCurrentMethod().Name);
						collection.Clear();
						trx.Rollback();
					}
				}
			}

			return collection;
		}

		public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			MembershipUserCollection collection = new MembershipUserCollection();
			totalRecords = 0;
			using (DBEngine dbe = DBEngine.GetInstance())
			{
				using (IDbTransaction trx = dbe.BeginTransaction())
				{
					try
					{
						dbe.Transaction = trx;
						totalRecords = Convert.ToInt32(dbe.ExecuteScalar("select count(*) from users where username like {0}", usernameToMatch));
						MembershipUserModel[] all = MembershipUserModel.FindBySql(trx, this.query.FindUsersByName, usernameToMatch, pageIndex, pageSize);
						foreach (var data in all)
						{
							data.ProviderName = base.Name;
							collection.Add((LightOneMembershipUser)data);
						}
					}
					catch (Exception except)
					{
						TreatException(except, MethodBase.GetCurrentMethod().Name);
						collection.Clear();
						trx.Rollback();
					}
				}
			}

			return collection;
		}

		public override MembershipUserCollection GetAllUsers(int pageIndex , int pageSize , out int totalRecords)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			MembershipUserCollection collection = new MembershipUserCollection();
			totalRecords = 0;
			using (DBEngine dbe = DBEngine.GetInstance())
			{
				using (IDbTransaction trx = dbe.BeginTransaction())
				{
					try
					{
						dbe.Transaction = trx;
						totalRecords = Convert.ToInt32(dbe.ExecuteScalar("select count(*) from users where ApplicationName = {0}",this.ApplicationName));
						MembershipUserModel[] all = MembershipUserModel.FindBySql(trx, this.query.GetAllUsers, pageIndex, pageSize);
						foreach (var data in all)
						{
							data.ProviderName = base.Name;
							collection.Add((LightOneMembershipUser) data);
						}
					}
					catch(Exception except)
					{
						TreatException(except, MethodBase.GetCurrentMethod().Name);
						collection.Clear();
						trx.Rollback();
					}
				}
			}

			return collection;
		}

		public override int GetNumberOfUsersOnline()
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			try
			{
				using (DBEngine dbe = DBEngine.GetInstance())
				{
					TimeSpan onlineSpan = new TimeSpan(0, System.Web.Security.Membership.UserIsOnlineTimeWindow, 0);
					DateTime compareTime = DateTime.Now.Subtract(onlineSpan);

					return Convert.ToInt32(dbe.ExecuteScalar("select count(*) from users where LastActivityDate > {0} and ApplicationName = {1}", compareTime, this.ApplicationName));
				}
			}
			catch (Exception except)
			{
				this.TreatException(except,MethodBase.GetCurrentMethod().Name);
				throw except;
			}
		}

		public override string GetPassword(string username, string answer)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			if (!EnablePasswordRetrieval)
			{
				throw new ProviderException("Password Retrieval Not Enabled.");
			}

			if (PasswordFormat == MembershipPasswordFormat.Hashed)
			{
				throw new ProviderException("Cannot retrieve Hashed passwords.");
			}

			try
			{
				var user = MembershipUserModel.FindByUserName(username, this.ApplicationName);
				using(DBEngine dbe = DBEngine.GetInstance())
				{
				return  UnEncodePassword(Convert.ToString(
					dbe.ExecuteScalar("select password from users where username={0} and answer={1}", username, answer)
					));
				}
			   
			}
			catch (Exception except)
			{
				this.TreatException(except, MethodBase.GetCurrentMethod().Name);
				throw except;
			}
		}

		public override MembershipUser GetUser(string username, bool userIsOnline)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
			logger.Log("\tParam:{0}->{1}", "username", username);
			logger.Log("\tParam:{0}->{1}", "userIsOnline", userIsOnline);
			logger.Log("\tVaiable:{0}->{1}", "this.ApplicationName", this.ApplicationName);
#endif
			var data = MembershipUserModel.FindByUserName(username, this.ApplicationName);
#if DEBUG
			logger.Log("MembershipUserModel.FindByUserName->{0}", data);
#endif

			if (data == null) return null;

			if (userIsOnline)
			{
				data.LastActivityDate = DateTime.Now;
				data.Save();
			}
			
			data.ProviderName = base.Name;
			LightOneMembershipUser user = data;
			
			return user;
		}

		public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
			logger.Log("\tParam:{0}->{1}", "providerUserKey", providerUserKey);
			logger.Log("\tParam:{0}->{1}", "userIsOnline", userIsOnline);
			logger.Log("\tproviderUserKey.Type:{0}", providerUserKey.GetType().Name);
#endif
			var data = MembershipUserModel.FindById((new Guid(providerUserKey.ToString())));
			if (data == null) return null;

			if (userIsOnline)
			{
				data.LastActivityDate = DateTime.Now;
				data.Save();
			}

			data.ProviderName = base.Name;
			return new LightOneMembershipUser(data);
		}

		public override string GetUserNameByEmail(string email)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			LightOneMembershipUser data = MembershipUserModel.FindByEmail(email);

			if (data != null)
			{
				return data.UserName;
			}
			return null;
		}

		public override string ResetPassword(string username, string answer)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			if (!EnablePasswordReset)
			{
				throw new NotSupportedException("Password reset is not enabled.");
			}

			if (answer == null && RequiresQuestionAndAnswer)
			{
				throw new ProviderException("Password answer required for password reset.");
			}

			string newPassword =
		System.Web.Security.Membership.GeneratePassword(newPasswordLength, MinRequiredNonAlphanumericCharacters);

			ValidatePasswordEventArgs args =
		new ValidatePasswordEventArgs(username, newPassword, true);

			OnValidatingPassword(args);

			if (args.Cancel)
			{
				if (args.FailureInformation != null)
				{
					throw args.FailureInformation;
				}
				else
				{
					throw new MembershipPasswordException("Reset password canceled due to password validation failure.");
				}
			}
			else
			{
				LightOneMembershipUser user = MembershipUserModel.FindByUserNameAnApplication(username, this.ApplicationName);
				if (user == null)
				{
					throw new MembershipPasswordException("The supplied user name is not found.");
				}
				else if (user.IsLockedOut)
				{
					throw new MembershipPasswordException("The supplied user is locked out.");
				}
				else if (RequiresQuestionAndAnswer && string.IsNullOrWhiteSpace(user.GetPassword(EncodePassword(answer))))
				{
					throw new MembershipPasswordException("Incorrect password answer.");
				}

				using (DBEngine dbe = DBEngine.GetInstance())
				{
					if (Convert.ToInt16(dbe.ExecuteNonQuery("update Users set password={0} where Id={1} and applicationName={2}", EncodePassword(newPassword), user.ProviderUserKey, this.ApplicationName)) > 0)
					{
						return newPassword;
					}
					else
					{
						throw new MembershipPasswordException("The supplied user is not found or database user has no privileges to do this operation.");
					}
				}
			}
		}

		public override bool UnlockUser(string userName)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			try
			{
				using (DBEngine dbe = DBEngine.GetInstance())
				{
					dbe.ExecuteNonQuery("UPDATE Users SET IsLockedOut = False, LastLockedOutDate = GetDate() WHERE Username = {0} AND ApplicationName = {1}", userName, this.ApplicationName);
					return true;
				}
			}
			catch (Exception except)
			{
				this.TreatException(except, MethodBase.GetCurrentMethod().Name);
				throw except;
			}
		}

		public override void UpdateUser(MembershipUser user)
		{

#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif
			if (!(user is LightOneMembershipUser)) throw new InvalidCastException("type of user doesn't is LightOneMembershipUser");

			((LightOneMembershipUser)user).Save();
		}

		#region Documentation
		/// <summary>
		/// Realiza a validação do usuário.
		/// </summary>
		#endregion
		public override bool ValidateUser(string username, string password)
		{
#if DEBUG
			logger.Log("Method:{0}", MethodInfo.GetCurrentMethod().Name);
#endif

			try
			{
				const string sql = "select count(*) from users where username={0} and islockedout = 0 and applicationname={1} and password={2}";

				using (DBEngine dbe = DBEngine.GetInstance())
				{
					short count = Convert.ToInt16(dbe.ExecuteScalar(sql, username, this.ApplicationName, EncodePassword(password)));
					if (count > 0)
					{
						return true;
					}
					else
					{
						return false;
					}
				}
			}
			catch(Exception except)
			{
				this.TreatException(except, MethodBase.GetCurrentMethod().Name);
				return false;
			}
		}

		#region Documentation
		/// <summary>
		/// Cria uma entrada no registro de eventos do Windows.
		/// </summary>
		#endregion
		private void TreatException(Exception e, string action)
		{
			if (this.WriteExceptionsToEventLog)
			{
				EventLog log = new EventLog();
				log.Source = eventSource;
				log.Log = EVENT_LOG;

				string message = "An exception occurred communicating with the data source.\n\n";
				message += "Action: " + action + "\n\n";
				message += "Exception: " + e.ToString();

				log.WriteEntry(message);
			}
			else
			{
				throw new Exception(string.Format("{0}:{1}", action, e.Message), e);
			}
		}

		#endregion
	}
}